/*****************************************************************************//** 
 *  \brief     Factory Class
 *  \author    Brian Rose
 *  \version   1.0
 *  \date      2012
 *  \copyright GNU Public License.
 *	\notes
******************************************************************************/
/*** Header Guard ************************************************************/
#ifndef NFACTORY_H
#define NFACTORY_H
/*** Includes *****************************************************************/
/*Stl*/
#include <hash_map>
#include <assert.h>
/*Engine*/
#include "NeroCore\NMemoryManager.h"
#include "NeroCore\NLogger.h"

/*** Forward Declarations *****************************************************/
/*** Macros *******************************************************************/
#define ConcreteFactory( FactoryName, AbstractProductT, ConcreteProductT )  \
    class FactoryName ## NFactory : Utility::NFactory<AbstractProductT>				\
    {																		\
        public:																\
             FactoryName ## NFactory()  { Add   ( this ) ; }				\
            ~FactoryName ## NFactory()  { Remove( this ) ; }				\
            virtual const char * GetClassName( void )						\
			{ return # FactoryName ; }										\
            virtual AbstractProductT*  Create( void ) const					\
            {																\
                AbstractProductT * product = MY_NEW( Memory::HID_Game,# ConcreteProductT ) ConcreteProductT() ; \
                return product ;                                        \
            }                                                           \
	};
/*** Namespace ****************************************************************/
namespace Utility
{
/*** Constants ****************************************************************/
/*** Interface ****************************************************************//** 
 *  \brief     NFactory Class
 *  \details   Used to Create Instances of Classes
 ******************************************************************************/
template <class T> class NFactory
{
private:
	typedef std::pair <std::string, NFactory *>			FactoryEntry;
	typedef std::hash_map <std::string, NFactory *>		FactoryList;
	typedef	typename FactoryList::iterator				FactoryIterator;
	
public:	
	/**************************************************************************//** 
	*	\brief		Deconstructor
	*	\param		- void
	*	\return		- void
	******************************************************************************/
	virtual ~NFactory()
	{
		mFactories.clear();
	}

	/**************************************************************************//** 
	*	\brief		Finds an associated concrete factory by name
	*	\param		className	- name of class to find
	*	\return		NFactory*	- NFactory paried with name
	******************************************************************************/
	static NFactory* Find(std::string& className)
	{
		if(mFactories.Find(className) != mFactories.End())
		{	
			return mFactories.Find(className);	
		}
		return NULL;
	}

	/**************************************************************************//** 
	*	\brief		Return a new object of given type by name
	*	\param		- void
	*	\return		T - new instance of class
	******************************************************************************/
	virtual T* Create(void) const = 0;

	/**************************************************************************//** 
	*	\brief		Return an Iterator for the container of factories
	*	\param		- void
	*	\return		FactoryIterator iterator of hashmap of iterator
	******************************************************************************/
	static FactoryIterator Begin()
	{
		return mFactories.begin();
	}

	/**************************************************************************//** 
	*	\brief		Return an iterator past the last factory in the container
	*	\param		- void
	*	\return		FactoryIterator	- iterator pointing to the end of the list of factories
	******************************************************************************/
	static FactoryIterator End()
	{
		return mFactories.end();
	}

	/**************************************************************************//** 
	*	\brief		Add factory to hash map
	*	\param		aFactor - the factory to had to the hash map
	*	\return		- void
	******************************************************************************/
	static void Add(const NFactory* aFactor)
	{
		std::string factoryName = const_cast<NFactory *>(aFactor)->GetClassName();
		mFactories.insert(FactoryEntry(factoryName, const_cast<NFactory *>(aFactor)));
	}

	/**************************************************************************//** 
	*	\brief		Remove concrete factory given from list
	*	\param		aFactor - the factory to had to the hash map
	*	\return		- void
	******************************************************************************/
	static void Remove(const NFactory*)
	{
		//throw Engine::NException("Factory::Remove::Not Implemented");
	}

	/**************************************************************************//** 
	*	\brief		Returns the address of a concrete 
					product associated with this concrete factory
	*	\param		className name of the class to create
	*	\return		T abstract class that is created
	******************************************************************************/
	static T* Create(const std::string& className )
	{
		if(mFactories[className] != NULL)
		{
			return mFactories[className]->Create();
		}
		return NULL;
	}

	/**************************************************************************//** 
	*	\brief		Get name of class handles by factory
	*	\param		- void
	*	\return		const char * - name of class
	******************************************************************************/
	virtual const char * GetClassName(void) = 0;

	static FactoryList mFactories; //!< hashmap of all factories with the same parent
};	
template <typename T>
typename NFactory<T>::FactoryList NFactory<T>::mFactories = NFactory<T>::FactoryList();

}
#endif //NFactory